package test;

import java.util.logging.Logger;

import utils.RotateMatrix;

import com.jme3.app.Application;
import com.jme3.collision.MotionAllowedListener;
import com.jme3.input.InputManager;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseAxisTrigger;
import com.jme3.math.Matrix3f;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;

/**
 * A first person view camera controller. After creation, you must register the
 * camera controller with the dispatcher using #registerWithDispatcher().
 * 
 * Controls: - Move the mouse to rotate the camera - Mouse wheel for zooming in
 * or out - WASD keys for moving forward/backward and strafing - QZ keys raise
 * or lower the camera
 */
public class OwnCamera implements AnalogListener, ActionListener {
	private static final Logger logger = Logger.getLogger(Application.class
			.getName());

	private Camera cam;
	private Vector3f initialUpVec;
	private MotionAllowedListener motionAllowed = null;
	private boolean enabled = true;
	private InputManager inputManager;

	private double rotationSpeed = 1.0;
	private float moveSpeed = 3f;
	private float zoomSpeed = 1f;

	public OwnCamera(Camera cam) {
		this.cam = cam;
		initialUpVec = cam.getUp().clone();
		
		logger.info("created Own Camera");
	}

	/**
	 * Sets the up vector that should be used for the camera.
	 * 
	 * @param upVec
	 */
	public void setUpVector(Vector3f upVec) {
		initialUpVec.set(upVec);
	}

	public void setMotionAllowedListener(MotionAllowedListener listener) {
		this.motionAllowed = listener;
	}

	/**
	 * Sets the move speed. The speed is given in world units per second.
	 * 
	 * @param moveSpeed
	 */
	public void setMoveSpeed(float moveSpeed) {
		this.moveSpeed = moveSpeed;
	}

	/**
	 * Sets the rotation speed.
	 * 
	 * @param rotationSpeed
	 */
	public void setRotationSpeed(double rotationSpeed) {
		this.rotationSpeed = rotationSpeed;
	}

	/**
	 * Sets the zoom speed.
	 * 
	 * @param zoomSpeed
	 */
	public void setZoomSpeed(float zoomSpeed) {
		this.zoomSpeed = zoomSpeed;
	}

	/**
	 * @param enable
	 *            If false, the camera will ignore input.
	 */
	public void setEnabled(boolean enable) {
		if (enabled && !enable) {
				inputManager.setCursorVisible(true);
		}
		enabled = enable;
	}

	/**
	 * @return If enabled
	 * @see OwnCamera#setEnabled(boolean)
	 */
	public boolean isEnabled() {
		return enabled;
	}

	/**
	 * Registers the OwnCamera to receive input events from the provided
	 * Dispatcher.
	 * 
	 * @param dispacher
	 */
	public void registerWithInput(InputManager inputManager) {
		this.inputManager = inputManager;

		String[] mappings = new String[] { "Left", "Right", "Up", "Down",
		"StrafeLeft", "StrafeRight", "Forward", "Backward",
		"ZoomIn", "ZoomOut", "RotateDrag"};
		
        inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_DELETE));
        inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_PGDN));
        //inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_P));
        //inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_END));

		inputManager.addMapping("ZoomIn", new MouseAxisTrigger(2, false));
		inputManager.addMapping("ZoomOut", new MouseAxisTrigger(2, true));

		inputManager
				.addMapping("StrafeLeft", new KeyTrigger(KeyInput.KEY_LEFT));
		inputManager.addMapping("StrafeRight", new KeyTrigger(
				KeyInput.KEY_RIGHT));
		inputManager.addMapping("Forward", new KeyTrigger(KeyInput.KEY_UP));
		inputManager.addMapping("Backward", new KeyTrigger(KeyInput.KEY_DOWN));

		inputManager.addListener(this, mappings);
		inputManager.setCursorVisible(true);
	}

	private void rotateCamera(float value, Vector3f axis) {
		double alpha = value;
		alpha *= rotationSpeed;
		
		Matrix3f mat = new Matrix3f();
		mat.fromAngleNormalAxis(0, axis);
		
		RotateMatrix.getRotationZ(alpha).mult(mat, mat);
		Vector3f up = cam.getUp();
		Vector3f left = cam.getLeft();
		Vector3f dir = cam.getDirection();

		mat.mult(up, up);
		mat.mult(left, left);
		mat.mult(dir, dir);
				
		Quaternion q = new Quaternion();
		q.fromAxes(left, up, dir);
		q.normalize();
		
		
		
		cam.setAxes(q);		
	}

	private void zoomCamera(float value) {
		Vector3f location = cam.getLocation();
		if (location.z > 5 && value < 0)
			location.z -= zoomSpeed;
		if (location.z < 40 && value > 0)
			location.z += zoomSpeed;
		cam.setLocation(location);
	}

	private void moveCamera(float value, boolean sideways) {
		Vector3f vel = new Vector3f();
		Vector3f pos = cam.getLocation().clone();

		if (sideways) {
			cam.getLeft(vel);
		} else {
			cam.getUp(vel);
		}
		vel.multLocal(value * moveSpeed);

		if (motionAllowed != null)
			motionAllowed.checkMotionAllowed(pos, vel);
		else
			pos.addLocal(vel);

		cam.setLocation(pos);
	}

	public void onAnalog(String name, float value, float tpf) {
		if (!enabled)
			return;

		if (name.equals("Left")) {
			rotateCamera(value, initialUpVec);
		} else if (name.equals("Right")) {
			rotateCamera(-value, initialUpVec);
		} else if (name.equals("Up")) {
			rotateCamera(-value, cam.getLeft());
		} else if (name.equals("Down")) {
			rotateCamera(value, cam.getLeft());
		} else if (name.equals("Forward")) {
			moveCamera(value, false);
		} else if (name.equals("Backward")) {
			moveCamera(-value, false);
		} else if (name.equals("StrafeLeft")) {
			moveCamera(value, true);
		} else if (name.equals("StrafeRight")) {
			moveCamera(-value, true);
		} else if (name.equals("ZoomIn")) {
			zoomCamera(value);
		} else if (name.equals("ZoomOut")) {
			zoomCamera(-value);
		}
	}

	public void onAction(String name, boolean value, float tpf) {
		if (!enabled)
			return;
	}
}
